package com.sikaryofficial.backend.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sikaryofficial.backend.config.ActivityConfig;
import com.sikaryofficial.backend.config.TenantFilterProperties;
import com.sikaryofficial.backend.constant.ActivityConstant;
import com.sikaryofficial.backend.constant.ActivityReserveStatusEnum;
import com.sikaryofficial.backend.constant.ActivityStatusEnum;
import com.sikaryofficial.backend.constant.ActivityTypeEnum;
import com.sikaryofficial.backend.constant.MyStatisticsTypeEnum;
import com.sikaryofficial.backend.constant.RedisCacheKey;
import com.sikaryofficial.backend.domain.dto.req.prize.LotteryReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityBrandSpecialListReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityDeleteReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityListReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityRecommendListReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityReserveListReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityReserveReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityResultListReq;
import com.sikaryofficial.backend.domain.dto.req.productactivity.ActivityResultReq;
import com.sikaryofficial.backend.domain.dto.resp.AddressUserListDTO;
import com.sikaryofficial.backend.domain.dto.resp.productactivity.ActivityDTO;
import com.sikaryofficial.backend.domain.dto.resp.productactivity.ActivityReserveDTO;
import com.sikaryofficial.backend.domain.dto.resp.productactivity.ActivityResultDTO;
import com.sikaryofficial.backend.domain.dto.resp.productactivity.ActivityResultNoMaskDTO;
import com.sikaryofficial.backend.domain.entity.Activity;
import com.sikaryofficial.backend.domain.entity.ActivityAttendRec;
import com.sikaryofficial.backend.domain.entity.ActivityDelivery;
import com.sikaryofficial.backend.domain.entity.ActivityPrizeRelation;
import com.sikaryofficial.backend.domain.entity.ActivityReserve;
import com.sikaryofficial.backend.domain.entity.ActivityResult;
import com.sikaryofficial.backend.domain.entity.MyStatistics;
import com.sikaryofficial.backend.domain.entity.Prize;
import com.sikaryofficial.backend.domain.entity.UserAction;
import com.sikaryofficial.backend.domain.mapping.ActivityMapping;
import com.sikaryofficial.backend.domain.mapping.ActivityResultMapping;
import com.sikaryofficial.backend.domain.vo.ObjectCount;
import com.sikaryofficial.backend.domain.vo.PointConsumerVO;
import com.sikaryofficial.backend.domain.vo.export.ActivityExportVO;
import com.sikaryofficial.backend.message.body.ActivityAttendMessage;
import com.sikaryofficial.backend.message.product.ProductProcess;
import com.sikaryofficial.backend.resolver.PropertiesTools;
import com.sikaryofficial.backend.service.IActivityAttendRecService;
import com.sikaryofficial.backend.service.IActivityDeliveryService;
import com.sikaryofficial.backend.service.IActivityPrizeRelationService;
import com.sikaryofficial.backend.service.IActivityReserveService;
import com.sikaryofficial.backend.service.IActivityResultService;
import com.sikaryofficial.backend.service.IActivityService;
import com.sikaryofficial.backend.service.IAttachmentService;
import com.sikaryofficial.backend.service.IMyStatisticsService;
import com.sikaryofficial.backend.service.IPointsDetailStrategy;
import com.sikaryofficial.backend.service.IPrizeService;
import com.sikaryofficial.backend.service.IUserActionService;
import com.sikaryofficial.backend.service.IUserAddressService;
import com.sikaryofficial.backend.service.IUserCertificateService;
import com.sikaryofficial.backend.service.activity.ActivityCacheService;
import com.sikaryofficial.backend.service.activity.lottery.LotteryEngine;
import com.sikaryofficial.backend.service.prize.cache.PrizeActivityCacheService;
import com.sikaryofficial.backend.service.statistics.MyStatisticsEvent;
import com.sikaryofficial.backend.service.statistics.MyStatisticsProcessor;
import com.sikaryofficial.backend.service.user.UserCacheService;
import com.sikaryofficial.common.core.constant.PointRuleConstant;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.utils.DateUtils;
import com.sikaryofficial.common.core.web.page.PageDomain;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.model.att.AttachmentResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.sikaryofficial.backend.constant.ActivityConstant.EFFECT_YES;
import static com.sikaryofficial.backend.constant.ActivityConstant.OFF_LINE;
import static com.sikaryofficial.backend.constant.RedisCacheKey.ACTIVITY_ATTEND_RESULT_CACHE;
import static com.sikaryofficial.backend.constant.RedisCacheKey.ACTIVITY_RESERVE_STATUS_CACHE;
import static com.sikaryofficial.backend.constant.i18n.ExceptionConstant.*;

/**
 * @author : qinjinyuan
 * @desc : 活动管理服务
 * @date : 2023/10/30 15:39
 */
@Service
@Slf4j
@SuppressWarnings(value = "unchecked")
public class ActivityManager {
    private static final String LAST_SQL = " limit 1 ";
    @Autowired
    private IActivityReserveService activityReserveService;
    @Autowired
    private IActivityResultService activityResultService;
    @Autowired
    private IActivityService activityService;
    @Autowired
    private PrizeActivityCacheService prizeActivityCacheService;
    @Autowired
    private IPrizeService prizeService;
    @Resource
    private Map<String, IPointsDetailStrategy> pointsDetailStrategyMap;
    @Autowired
    private MyStatisticsProcessor myListener;
    @Autowired
    private IAttachmentService attachmentService;
    @Autowired
    private ProductProcess productProcess;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IMyStatisticsService myStatisticsService;
    @Resource
    private TenantFilterProperties tenantFilterProperties;
    @Value("${hismk.backend.activity.reserve.statusSaveDay}")
    private Long statusSaveDay;
    @Autowired
    private IUserActionService userActionService;
    @Autowired
    private IActivityDeliveryService activityDeliveryService;
    @Autowired
    private IUserAddressService userAddressService;
    @Autowired
    private IUserCertificateService userCertificateService;
    @Resource
    private PropertiesTools propertiesTools;
    @Autowired
    private ActivityCacheService activityCacheService;
    @Autowired
    private IActivityPrizeRelationService relationService;
    @Autowired
    private LotteryEngine lotteryEngine;
    @Autowired
    private ActivitySettingManager activitySettingManager;
    @Resource
    private ActivityConfig activityConfig;
    @Autowired
    private UserCacheService userCacheService;
    @Autowired
    private IActivityAttendRecService attendRecService;

    /**
     * 获取首页活动奖品列表
     */
    public IPage<ActivityDTO> getActivityPrizeList(ActivityRecommendListReq req) {
        IPage<ActivityDTO> activityDTOIPage = prizeActivityCacheService.getActivityList(req);
        if (Objects.isNull(activityDTOIPage)) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        // 抢购结果数据填充
        activityDTOIPage.getRecords().forEach(activityDTO -> activityDTO.setAttendResult(attendResult(activityDTO.getActivityId())));
        return activityDTOIPage;
    }

    /**
     * 获取关联活动列表
     */
    public IPage<ActivityDTO> recommendList(ActivityRecommendListReq req) {
        IPage<ActivityDTO> activityDTOIPage = prizeActivityCacheService.recommendList(req);
        if (Objects.isNull(activityDTOIPage)) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        // 抢购结果数据填充
        activityDTOIPage.getRecords().forEach(activityDTO -> activityDTO.setAttendResult(attendResult(activityDTO.getActivityId())));
        return activityDTOIPage;
    }

    public IPage<ActivityDTO> activityListByBrandSpecial(ActivityBrandSpecialListReq req) {
        IPage<ActivityDTO> activityDTOIPage = prizeActivityCacheService.getActivityListByBrandSpecial(req);
        if (Objects.isNull(activityDTOIPage)) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        // 抢购结果数据填充
        activityDTOIPage.getRecords().forEach(activityDTO -> activityDTO.setAttendResult(attendResult(activityDTO.getActivityId())));
        return activityDTOIPage;
    }

    /**
     * 线下活动预约保存
     *
     * @param req
     * @return
     */
    public boolean reserveOfflineSave(ActivityReserveReq req) {
        // 判定是否已预约过
        if (hasReserve(req.getActivityId()) > 0) {
            return true;
        }
        Long userId = SecurityUtils.getUserId();
        Long activityReserveId = IdWorker.getId();
        Activity activity = activityService.getOne(new LambdaQueryWrapper<Activity>()
                .eq(Activity::getActivityId, req.getActivityId())
                .eq(Activity::getDeletedVersion, 0L)
                .eq(Activity::getHasEffect, EFFECT_YES)
        );
        if (Objects.isNull(activity)) {
            log.info("活动不存在");
            return true;
        }
        // 活动类型校验
        if (!Objects.equals(activity.getHasOnline(), OFF_LINE)) {
            log.info("非线下活动");
            return true;
        }
        // 2-查询是否已经预约过
        if (activityReserveService.count(new LambdaQueryWrapper<ActivityReserve>()
                .eq(ActivityReserve::getCreatedBy, userId)
                .eq(ActivityReserve::getActivityId, req.getActivityId())
                .eq(ActivityReserve::getDeletedVersion, 0L)
        ) > 0) {
            return true;
        }
        // 3-新增预约记录
        ActivityReserve activityReserve = ActivityReserve.builder()
                .activityReserveId(activityReserveId)
                .createdBy(userId)
                .createdTime(new Date())
                .activityId(req.getActivityId())
                .reserveStatus(ActivityReserveStatusEnum.RESERVED.getCode())
                .reserveDate(new Date())
                .build();
        activityReserveService.save(activityReserve);
        // 4 添加预约数量统计
        myListener.addMyStatistics(new MyStatisticsEvent(MyStatisticsTypeEnum.RESERVATION_NUM.getBusinessType(), userId, activityReserveId));
        // 5-设置预约缓存 --1天
        redisService.setCacheObject(buildReserveStatusKey(req.getActivityId()), ActivityReserveStatusEnum.RESERVED.getCode()
                , statusSaveDay, TimeUnit.DAYS);
        return true;
    }

    /**
     * 保存活动预约
     *
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean reserveSave(ActivityReserveReq req) {
        // 判定是否已预约过
        if (hasReserve(req.getActivityId()) > 0) {
            throw new ServiceException(propertiesTools.getProperties(RESERVE_CONFIRMED_MSG));
        }
        Long userId = SecurityUtils.getUserId();
        IPointsDetailStrategy pointsDetailStrategy = pointsDetailStrategyMap.get(PointRuleConstant.RESERVE_PURCHASE + IPointsDetailStrategy.SUFFIX);
        if (Objects.isNull(userId) || userId <= 0L) {
            throw new ServiceException(propertiesTools.getProperties(RESERVE_CONFIRMED_MSG));
        }
        // 1、校验
        pointsDetailStrategy.beforeIncrease(userId);
        Long activityReserveId = IdWorker.getId();
        Activity activity = activityService.getOne(new LambdaQueryWrapper<Activity>()
                .eq(Activity::getActivityId, req.getActivityId())
                .eq(Activity::getDeletedVersion, 0L)
                .eq(Activity::getHasEffect, EFFECT_YES)
        );
        if (Objects.isNull(activity)) {
            throw new ServiceException(propertiesTools.getProperties(ACTIVITY_TAKEN_DOWN));
        }
        // 活动类型校验
        if (!StringUtils.equalsAny(activity.getActivityType().toString(),
                ActivityTypeEnum.PURCHASE_ACTIVATE_NOW.getCode().toString(),
                ActivityTypeEnum.PURCHASE.getCode().toString())) {
            throw new ServiceException(propertiesTools.getProperties(PARAM_ERROR));
        }
        // 活动进行中，不能进行预约
        if (new Date().compareTo(activity.getStartTime()) >= 0) {
            throw new ServiceException(propertiesTools.getProperties(VERIFY_RESERVE_MSG));
        }
        // 2-查询是否已经预约过
        if (activityReserveService.count(new LambdaQueryWrapper<ActivityReserve>()
                .eq(ActivityReserve::getCreatedBy, userId)
                .eq(ActivityReserve::getActivityId, req.getActivityId())
                .eq(ActivityReserve::getDeletedVersion, 0L)
        ) > 0) {
            throw new ServiceException(propertiesTools.getProperties(RESERVE_CONFIRMED_MSG));
        }
        // 3-新增预约记录
        ActivityReserve activityReserve = ActivityReserve.builder()
                .activityReserveId(activityReserveId)
                .createdBy(userId)
                .createdTime(new Date())
                .activityId(req.getActivityId())
                .reserveStatus(ActivityReserveStatusEnum.RESERVED.getCode())
                .reserveDate(new Date())
                .build();
        activityReserveService.save(activityReserve);
        // 4 添加预约数量统计
        myListener.addMyStatistics(new MyStatisticsEvent(MyStatisticsTypeEnum.RESERVATION_NUM.getBusinessType(), userId, activityReserveId));
        // 5-积分扣减
        pointsDetailStrategy.updatePointsByInputValue(activityReserveId, userId, activity.getPoints(), null, activity.getActivityName());
        pointsDetailStrategy.afterIncrease(userId);
        // 6-设置预约缓存 --1天
        redisService.setCacheObject(buildReserveStatusKey(req.getActivityId()), ActivityReserveStatusEnum.RESERVED.getCode()
                , statusSaveDay, TimeUnit.DAYS);
        return true;
    }

    /**
     * 构建活动预约缓存key
     *
     * @param activityId
     * @return
     */
    private String buildReserveStatusKey(Long activityId) {
        return MessageFormat.format(ACTIVITY_RESERVE_STATUS_CACHE, activityId, SecurityUtils.getUserId());
    }

    /**
     * 获取活动预约列表
     *
     * @param reserveListReq
     * @return
     */
    public IPage<ActivityReserveDTO> reserveList(ActivityReserveListReq reserveListReq) {
        Integer reserveStatus = reserveListReq.getReserveStatus();
        Page<ActivityReserveDTO> customerPage = new Page<>(reserveListReq.getPageNum(), reserveListReq.getPageSize());
        ActivityReserveStatusEnum reserveStatusEnum = ActivityReserveStatusEnum.getByCode(reserveStatus);
        if (Objects.isNull(reserveStatusEnum)) {
            throw new ServiceException("reserveStatus is not exist.");
        }
        IPage<ActivityReserveDTO> pageData = activityReserveService.reserveList(customerPage, SecurityUtils.getUserId(), reserveStatus);
        if (CollUtil.isEmpty(pageData.getRecords())) {
            return pageData;
        }
        List<Long> objectIds = pageData.getRecords().stream().map(ActivityReserveDTO::getActivityId).collect(Collectors.toList());
        // 附件列表
        Map<Long, List<AttachmentResp>> attachmentMap = attachmentService.getAttachmentByIds(objectIds);
        // 设置附件集合
        pageData.getRecords().forEach(activityDTO -> {
            List<AttachmentResp> attachmentList = attachmentMap.get(activityDTO.getActivityId());
            // 品牌名称
            activityDTO.setPrizeBrand(tenantFilterProperties.getBrandByTenantId(activityDTO.getTenantId()));
            if (CollUtil.isNotEmpty(attachmentList)) {
                activityDTO.setCoverUrlList(attachmentList.stream().filter(attachmentResp -> attachmentResp.getHasCover() == 1).collect(Collectors.toList()));
            }
        });
        return pageData;
    }

    /**
     * 保存活动
     *
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long activitySave(ActivityReq req) {
        // 1、保存活动
        Activity activity = ActivityMapping.INSTANCE.coverReqToEntity(req);
        Integer tenantId = tenantFilterProperties.getTenantByBrand(req.getActivityBrand());
        activity.setTenantId(tenantId);
        Long activityId = IdWorker.getId();
        if (req.isUseInPreView()) {
            // 1、保存活动
            activity.setActivityId(activityId);
            activity.setCreatedBy(SecurityUtils.getUserId());
            activity.setCreatedTime(new Date());
            activity.setDeletedVersion(activityId);
            activityService.save(activity);
            // 2、保存预览活动的附件
            processAttachment(req, activity);
            return activityId;
        }

        if (Objects.isNull(ActivityTypeEnum.getNameByCode(req.getActivityType()))) {
            throw new ServiceException("activityType is not exist.");
        }
        if (Objects.isNull(activity.getActivityId()) || Objects.equals(activity.getActivityId(), 0L)) {
            // 检查活动开始时间
            if (LocalDateTimeUtil.of(new Date()).compareTo(LocalDateTimeUtil.of(req.getStartTime())) > 0) {
                throw new ServiceException("activity start time is invalid");
            }
            // 保存
            activity.setActivityId(activityId);
            activity.setCreatedBy(SecurityUtils.getUserId());
            activity.setCreatedTime(new Date());
            activityService.save(activity);
        } else {
            /**
             * 活动已上架，不能修改
             */
            if (Objects.equals(EFFECT_YES, activity.getHasEffect())) {
                throw new ServiceException("上架状态的活动不能进行操作,请先下架");
            }
            activityId = activity.getActivityId();
            activity.setUpdatedBy(SecurityUtils.getUserId());
            activity.setUpdatedTime(new Date());
            activityService.update(activity, new LambdaQueryWrapper<Activity>()
                    .eq(Activity::getActivityId, activity.getActivityId())
                    .eq(Activity::getDeletedVersion, 0L)
            );
            // 删除附件
            attachmentService.deleteAttachmentList(activity.getActivityId(), SecurityUtils.getUserId());
        }
        // 2、保存附件
        processAttachment(req, activity);
        return activityId;
    }

    private void processAttachment(ActivityReq req, Activity activity) {
        attachmentService.saveAttachmentList(activity.getActivityId(), req.getAttachmentList(), SecurityUtils.getUserId());
    }

    /**
     * 活动上架
     *
     * @param activityId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean activityEffect(Long activityId, Integer opt) {
        if (ActivityConstant.EFFECT_NO.equals(opt)) {
            effectUp(activityId);
        }
        if (ActivityConstant.EFFECT_YES.equals(opt)) {
            effectDown(activityId);
        }
        return true;
    }

    private void effectDown(Long activityId) {
        Activity activity = activityService.getOne(new LambdaQueryWrapper<Activity>()
                .eq(Activity::getActivityId, activityId)
                .eq(Activity::getDeletedVersion, 0L)
                .eq(Activity::getHasEffect, ActivityConstant.EFFECT_YES)
        );
        // 获取活动上架状态
        if (Objects.isNull(activity)) {
            throw new ServiceException("活动不存在或活动已下架");
        }

        Optional<Activity> optional = Optional.of(activity);
        optional.ifPresent(a -> {
            // 1、更新上架状态
            a.setHasEffect(ActivityConstant.EFFECT_NO);
            a.setUpdatedBy(SecurityUtils.getUserId());
            a.setUpdatedTime(new Date());
            activityService.updateById(a);
        });
        // 清除存缓存数据
        activityCacheService.cleanStockCache(activityId);
        // 设置基本信息
        activityCacheService.updateActivityBaseInfo(activityId, activity);
    }

    private void effectUp(Long activityId) {
        Activity activity = activityService.getOne(new LambdaQueryWrapper<Activity>()
                .eq(Activity::getActivityId, activityId)
                .eq(Activity::getDeletedVersion, 0L)
                .eq(Activity::getHasEffect, ActivityConstant.EFFECT_NO)
        );
        // 获取活动上架状态
        if (Objects.isNull(activity)) {
            throw new ServiceException("活动不存在或者无效");
        }
        List<ActivityPrizeRelation> relations = relationService.list(new LambdaQueryWrapper<ActivityPrizeRelation>()
                .select(ActivityPrizeRelation::getActivityPrizeId)
                .eq(ActivityPrizeRelation::getActivityId, activityId)
                .eq(ActivityPrizeRelation::getDeletedVersion, 0L)
        );
        // 数据校验
        validData(activityId, activity, relations);
        Optional<Activity> optional = Optional.of(activity);
        optional.ifPresent(a -> {
            // 1、更新上架状态
            a.setHasEffect(EFFECT_YES);
            a.setUpdatedBy(SecurityUtils.getUserId());
            a.setUpdatedTime(new Date());
            activityService.updateById(a);
        });
        // 库存缓存设置
        activityCacheService.setStockCache(activityId);
        // 设置基本信息
        activityCacheService.updateActivityBaseInfo(activityId, activity);

        // 活动上架-更新实物获奖列表
        activityCacheService.updateActivityResultCacheVOS(activityId,relationService.getActivityResultCacheList(Collections.singletonList(activityId)));
    }

    private void validData(Long activityId, Activity activity, List<ActivityPrizeRelation> relations) {
        // 提前检查是否为线下活动，减少不必要的逻辑执行
        if (isOfflineActivity(activity)) {
            return;
        }
        // 线上活动的积分检查
        checkOnlineActivityPoints(activity);
        boolean checkWinRatioRelation = relationService.checkIfRelatedPrizesExceedOneHundred(activityId);
        boolean flag = isOnlineActivity(activity) && checkWinRatioRelation && CollUtil.isNotEmpty(relations);
        if (!flag) {
            throw new ServiceException("线上活动，奖项设置至少有一个，并且各奖项中奖概率之和要等于100");
        }
    }

    private boolean isOfflineActivity(Activity activity) {
        return Objects.equals(activity.getHasOnline(), ActivityConstant.OFF_LINE);
    }

    private boolean isOnlineActivity(Activity activity) {
        return Objects.equals(activity.getHasOnline(), ActivityConstant.ON_LINE);
    }

    private void checkOnlineActivityPoints(Activity activity) {
        if (isOnlineActivity(activity) && Objects.isNull(activity.getPoints())) {
            throw new ServiceException("线上活动，参与积分不能为空");
        } else if (isOnlineActivity(activity) && activity.getPoints() <= 0) {
            throw new ServiceException("线上活动，参与积分不能为0");
        }
    }

    /**
     * 删除活动
     *
     * @param req 删除参数
     * @return true / false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteActivity(ActivityDeleteReq req) {
        if (CharSequenceUtil.isBlank(req.getActivityIds())) {
            return false;
        }
        List<Long> activityIds = Lists.newArrayList();
        Arrays.asList(req.getActivityIds().split(",")).forEach(s -> activityIds.add(Long.valueOf(s)));
        activityIds.forEach(this::deleteById);

        // 判定活动是否存在已预约
        int count = activityReserveService.count(new LambdaQueryWrapper<ActivityReserve>()
                .in(ActivityReserve::getActivityId, req.getActivityIds())
                .eq(ActivityReserve::getDeletedVersion, 0L)
        );
        if (count > 0) {
            throw new ServiceException("If the activity has bean reserved, it cannot be deleted");
        }
        // 删除活动
        activityService.update(new LambdaUpdateWrapper<Activity>()
                .setSql("  deleted_version = activity_id ")
                .set(Activity::getUpdatedTime, new Date())
                .set(Activity::getUpdatedBy, SecurityUtils.getUserId())
                .in(Activity::getActivityId, activityIds)
        );
        // 删除奖品设置
        relationService.update(new LambdaUpdateWrapper<ActivityPrizeRelation>()
                .setSql("  deleted_version = activity_prize_id ")
                .set(ActivityPrizeRelation::getUpdatedTime, new Date())
                .set(ActivityPrizeRelation::getUpdatedBy, SecurityUtils.getUserId())
                .in(ActivityPrizeRelation::getActivityId, activityIds)
        );
        // 删除用户行为
        userActionService.update(new LambdaUpdateWrapper<UserAction>()
                .setSql("  deleted_version = user_action_id ")
                .set(UserAction::getUpdatedTime, new Date())
                .in(UserAction::getObjectId, activityIds));
        // 删除统计数据
        myStatisticsService.update(new LambdaUpdateWrapper<MyStatistics>()
                .setSql("  deleted_version = my_statistics_id ")
                .set(MyStatistics::getUpdatedTime, new Date())
                .in(MyStatistics::getObjectId, activityIds));
        return true;
    }

    /**
     * 删除活动
     *
     * @param activityId
     */
    private void deleteById(Long activityId) {
        // 1、删除活动 (是否是无条件删除)   活动已上架（生效的）
        Activity activity = activityService.getOne(new LambdaQueryWrapper<Activity>()
                .select(Activity::getHasEffect, Activity::getActivityId)
                .eq(Activity::getActivityId, activityId)
                .eq(Activity::getDeletedVersion, 0L)
                .last(LAST_SQL)
        );
        // 获取活动上架状态
        if (Objects.isNull(activity)) {
            throw new ServiceException("The current activity does not exist or does not meet the deletion criteria，activityId=" + activityId);
        }
        if (EFFECT_YES.equals(activity.getHasEffect())) {
            throw new ServiceException("The current activity has effect,could not be deleted.activityId=" + activityId);
        }
        // 2、删除活动
        activityService.updateById(Activity.builder()
                .activityId(activityId)
                .deletedVersion(activity.getActivityId())
                .updatedBy(SecurityUtils.getUserId())
                .updatedTime(new Date())
                .build());
    }

    /**
     * 活动详情
     *
     * @param activityId
     * @return
     */
    public ActivityDTO detail(Long activityId) {
        // TODO: 活动相关数据，后续可能需要从缓存中获取
        // TODO: 增加活动奖品设置列表
        // 1、查询活动
        ActivityDTO activityDTO = prizeService.getActivityDetail(activityId);
        if (Objects.isNull(activityDTO)) {
            throw new ServiceException("The current activity does not exist");
        }
        // 设置奖项数据
        activityDTO.setActivityPrizeRelationList(activitySettingManager.itemList(activityId));
        // 预约状态
        List<Long> objectIds = Lists.newArrayList();
        objectIds.add(activityDTO.getActivityId());
        Map<Long, Integer> reserveStatusMap = activityReserveService.getReserveStatus(objectIds, SecurityUtils.getUserId());
        activityDTO.setReserveStatus(MapUtils.getInteger(reserveStatusMap, activityDTO.getActivityId(), 0));
        // 2、查询附件
        List<AttachmentResp> attachmentList = attachmentService.getAttachmentById(activityId);
        if (CollUtil.isNotEmpty(attachmentList)) {
            activityDTO.setAttachmentList(attachmentList.stream().filter(attachmentResp -> attachmentResp.getHasCover() == 0).collect(Collectors.toList()));
            activityDTO.setCoverUrlList(attachmentList.stream().filter(attachmentResp -> attachmentResp.getHasCover() == 1).collect(Collectors.toList()));
        }
        activityDTO.setActivityBrand(tenantFilterProperties.getBrandByTenantId(activityDTO.getTenantId()));
        // 3、活动预约人数量
        activityDTO.setReserveNum(activityReserveService.count(new LambdaQueryWrapper<ActivityReserve>()
                .eq(ActivityReserve::getActivityId, activityId)
                .eq(ActivityReserve::getDeletedVersion, 0L)
                .eq(ActivityReserve::getReserveStatus, ActivityReserveStatusEnum.RESERVED.getCode())
        ));
        // 5、设置收藏
        Map<Integer, ObjectCount> myDataList = myStatisticsService.myCountMapByObjectIds(SecurityUtils.getUserId(), activityDTO.getActivityId());
        activityDTO.setHasOwnerCollection(Optional.ofNullable(myDataList.get(MyStatisticsTypeEnum.COLLECTION_ACTIVITY_NUM.getBusinessType())).map(ObjectCount::getObjectCount).orElse(0) > 0);
        return activityDTO;
    }



    /**
     * 活动列表
     *
     * @param req
     * @return
     */
    public IPage<ActivityDTO> activityList(ActivityListReq req) {
        Page<ActivityDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        // 活动平台-租户ID
        Integer tenantId = tenantFilterProperties.getTenantByBrand(req.getActivityBrand());
        req.setTenantId(tenantId);
        // 1、查询活动
        IPage<ActivityDTO> activityDTOIPage = prizeService.getBackendActivityList(customerPage, req);
        // 2、统计查询
        List<Long> activityIds = activityDTOIPage.getRecords().stream().map(ActivityDTO::getActivityId)
                .filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, Integer> reserveCountMap = activityReserveService.getReserveCountList(activityIds);
        Map<Long, Integer> purchaseCountMap = activityResultService.getPurchaseCountList(activityIds);

        // 3、设值具体数量
        AtomicInteger rowNum = new AtomicInteger(1);
        activityDTOIPage.getRecords().forEach(item -> {
            item.setSeqNo((req.getPageNum() - 1) * req.getPageSize() + rowNum.get());
            rowNum.getAndIncrement();
            item.setActivityBrand(tenantFilterProperties.getBrandByTenantId(item.getTenantId()));
            item.setReserveNum(MapUtils.getInteger(reserveCountMap, item.getActivityId(), 0));
            item.setPurchaseNum(MapUtils.getInteger(purchaseCountMap, item.getActivityId(), 0));
        });
        return activityDTOIPage;
    }

    private void setAttendDetails(ActivityDTO item, Map<Long, List<ActivityAttendRec>> activityAttendCountMap) {
        List<ActivityAttendRec> attendRecList = activityAttendCountMap.getOrDefault(item.getActivityId(), Collections.emptyList());
        item.setAttendNumber(attendRecList.size());

        List<Long> attendPeopleList = attendRecList.stream()
                .map(ActivityAttendRec::getCreatedBy)
                .distinct()
                .collect(Collectors.toList());
        item.setAttendPeopleNumber(attendPeopleList.size());
    }

    /**
     * 活动抽奖保存
     *
     * @param req 抽奖对象
     * @return 礼品ID
     */
    public Long lotterySave(LotteryReq req) {
        return lotteryEngine.doLottery(req);
    }

    /**
     * 活动抢购
     *
     * @param activityId
     * @return
     */
    public boolean attendSave(Long activityId) {
        Long activityPrizeId = attendVerify(activityId);
        Integer realStock = activityCacheService.getRealStock(activityId, activityPrizeId);
        Integer stockExt = activityCacheService.getStockExt(activityId, activityPrizeId);
        if ((Optional.ofNullable(realStock).orElse(0) == 0) && (Optional.ofNullable(stockExt).orElse(0) == 0)) {
            // 两个Optional对象都存在且值都等于0
            // 执行相应操作
            throw new ServiceException(propertiesTools.getProperties(ACTIVITY_INVENTORY_OUT));
        }
        ActivityAttendMessage message = new ActivityAttendMessage();
        message.setActivityId(activityId);
        message.setUserId(SecurityUtils.getUserId());
        message.setUserName(SecurityUtils.getUsername());
        message.setBrand(SecurityUtils.getBrand());
        message.setActivityPrizeId(activityPrizeId);
        return productProcess.sendDelayMessage(message);
    }

    /**
     * 活动参与检查，返回活动奖项ID
     *
     * @param activityId 活动ID
     * @return 活动奖项ID
     */
    private Long attendVerify(Long activityId) {
        if (Objects.isNull(activityId) || activityId <= 0L) {
            throw new ServiceException(propertiesTools.getProperties(PARAM_ERROR));
        }
        // 判定是否已抢购
        if (attendResult(activityId)) {
            throw new ServiceException(propertiesTools.getProperties(HAS_ATTEND_MSG));
        }
        // 抢购前判定下库存
        String relationCacheKey = MessageFormat.format(RedisCacheKey.ACTIVITY_RELATION_ID_CACHE, activityId.toString());
        List<PointConsumerVO> pointConsumerVOS = redisService.getCacheObject(relationCacheKey);
        Long activityPrizeId;
        if (CollUtil.isEmpty(pointConsumerVOS)) {
            throw new ServiceException(propertiesTools.getProperties(PARAM_ERROR));
        }
        PointConsumerVO pointConsumerVO = pointConsumerVOS.iterator().next();
        if (Objects.isNull(pointConsumerVO) || !StringUtils.equalsAny(
                pointConsumerVO.getActivityType().toString(),
                ActivityTypeEnum.PURCHASE_ACTIVATE_NOW.getCode().toString(),
                ActivityTypeEnum.PURCHASE.getCode().toString()
        )) {
            throw new ServiceException(propertiesTools.getProperties(PARAM_ERROR));
        }
        activityPrizeId = pointConsumerVO.getActivityPrizeId();
        return activityPrizeId;
    }

    /**
     * 活动是否报名
     *
     * @param activityId
     * @return 预约状态：1-reserved已预约, 2-expired已过期
     */
    public Integer hasReserve(Long activityId) {
        Integer reserveStatus = redisService.getCacheObject(buildReserveStatusKey(activityId));
        return Optional.ofNullable(reserveStatus).orElse(0);
    }

    /**
     * 活动抢购结果
     *
     * @param activityId 活动ID
     * @return true 抢购成功，false 抢购失败
     */
    public boolean attendResult(Long activityId) {
        // 抢购记录缓存
        Boolean result = redisService.getCacheObject(MessageFormat.format(ACTIVITY_ATTEND_RESULT_CACHE, activityId, SecurityUtils.getUserId()));
        return Objects.nonNull(result) && Boolean.TRUE.equals(result);
    }

    /**
     * 我的活动
     *
     * @param req
     * @return
     */
    public IPage<ActivityReserveDTO> myAwardsList(PageDomain req) {
        Page<ActivityReserveDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        IPage<ActivityReserveDTO> awardsList = activityReserveService.awardsList(customerPage, SecurityUtils.getUserId());
        if (Objects.isNull(awardsList) || CollUtil.isEmpty(awardsList.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        buildAttendResult(awardsList);
        return awardsList;
    }

    /**
     * 查询中奖列表
     *
     * @param awardsList
     * @return
     */
    private void buildAttendResult(IPage<ActivityReserveDTO> awardsList) {
        List<Long> activityIds = awardsList.getRecords().stream().map(ActivityReserveDTO::getActivityId).distinct().collect(Collectors.toList());
        if (CollUtil.isEmpty(activityIds)) {
            return;
        }
        // 查询参与结果数据
        Map<Long, ActivityResult> activityResultMap = activityResultService.getAttendResult(activityIds);
        // 附件列表
        Map<Long, List<AttachmentResp>> attachmentMap = attachmentService.getAttachmentByIds(activityIds);
        // 礼品信息
        List<Long> prizeIds = awardsList.getRecords().stream().map(ActivityReserveDTO::getPrizeId).distinct().collect(Collectors.toList());
        Map<Long, Prize> prizeMap = prizeService.getPrizeListByIds(prizeIds);
        awardsList.getRecords().forEach(item -> {
            Prize prize = MapUtils.getObject(prizeMap, item.getPrizeId());
            item.setPrizeBrand(Objects.isNull(prize) ? "" : prize.getPrizeBrand());
            item.setPrizeName(Objects.isNull(prize) ? "" : prize.getPrizeName());
            item.setAttendResult(Objects.nonNull(MapUtils.getObject(activityResultMap, item.getActivityId())));
            List<AttachmentResp> attachmentList = attachmentMap.get(item.getActivityId());
            if (CollUtil.isNotEmpty(attachmentList)) {
                item.setCoverUrlList(attachmentList.stream().filter(attachmentResp -> attachmentResp.getHasCover() == 1).collect(Collectors.toList()));
            }
        });
    }

    /**
     * 获奖名单
     *
     * @param req
     * @return
     */
    public IPage<ActivityResultDTO> publishAwardsList(ActivityResultListReq req) {
        Page<ActivityResult> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        IPage<ActivityResult> activityResultIPage = activityResultService.page(customerPage, new LambdaQueryWrapper<ActivityResult>()
                .select(ActivityResult::getActivityResultId, ActivityResult::getCreatedName, ActivityResult::getCreatedBy,
                        ActivityResult::getCreatedTime, ActivityResult::getPrizeGrantStatus)
                .eq(ActivityResult::getActivityId, req.getActivityId())
                .eq(ActivityResult::getDeletedVersion, 0L)
                .orderByDesc(ActivityResult::getCreatedTime)
        );
        if (CollUtil.isEmpty(activityResultIPage.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        return activityResultIPage.convert(ActivityResultMapping.INSTANCE::coverEntityToDTO);
    }

    /**
     * 获奖名单
     *
     * @param req
     * @return
     */
    public IPage<ActivityResultNoMaskDTO> publishAwardsNoMaskList(ActivityResultListReq req) {
        Page<ActivityResult> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        IPage<ActivityResult> activityResultIPage = activityResultService.page(customerPage, new LambdaQueryWrapper<ActivityResult>()
                .select(ActivityResult::getActivityResultId, ActivityResult::getCreatedName, ActivityResult::getCreatedBy,
                        ActivityResult::getCreatedTime, ActivityResult::getPrizeGrantStatus,
                        ActivityResult::getEmail, ActivityResult::getNickName
                )
                .eq(ActivityResult::getActivityId, req.getActivityId())
                .eq(ActivityResult::getDeletedVersion, 0L)
                .orderByDesc(ActivityResult::getCreatedTime)
        );
        if (CollUtil.isEmpty(activityResultIPage.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }

        IPage<ActivityResultNoMaskDTO> resultIPage = activityResultIPage.convert(ActivityResultMapping.INSTANCE::coverEntityToNoMaskDTO);
        List<Long> activityDeliveryIds = resultIPage.getRecords().stream().map(ActivityResultNoMaskDTO::getActivityResultId).collect(Collectors.toList());
        if (CollUtil.isEmpty(activityDeliveryIds)) {
            return resultIPage;
        }
        List<Long> userIds = resultIPage.getRecords().stream().map(ActivityResultNoMaskDTO::getCreatedBy).collect(Collectors.toList());
        // 21岁 证明数据查询
        Map<Long, String> proofMap = userCertificateService.get18thProofMap(new HashSet<>(userIds));

        // 发货地址快照查询
        List<ActivityDelivery> activityDeliveries = activityDeliveryService.list(new LambdaQueryWrapper<ActivityDelivery>()
                .eq(ActivityDelivery::getDeletedVersion, 0L)
                .in(ActivityDelivery::getObjectId, activityDeliveryIds));
        Map<Long, ActivityDelivery> addressDTOMap = activityDeliveries.stream().collect(Collectors.toMap(ActivityDelivery::getObjectId, Function.identity(), (k1, k2) -> k1));
        resultIPage.getRecords().forEach(item -> {
            ActivityDelivery delivery = MapUtils.getObject(addressDTOMap, item.getActivityResultId());
            item.setAddressObj(Objects.nonNull(delivery) ? delivery.getAddressObj() : null);
            if (Objects.nonNull(delivery) && Objects.nonNull(delivery.getAddressObj())) {
                AddressUserListDTO addressObj = delivery.getAddressObj();
                item.setFirstName(addressObj.getFirstName());
                item.setPhoneNumber(addressObj.getPhoneNumber());
                item.setLastName(addressObj.getLastName());
                item.setPostCode(addressObj.getPostCode());
                item.setDeliveryEmail(item.getAddressObj().getEmail());
                item.setAccount(item.getAddressObj().getPaymentAccount());
            }
            String url = MapUtils.getString(proofMap, item.getCreatedBy(), null);
            item.setProofUrl(url);
            item.setHasProofUrl(Objects.nonNull(url));
        });
        return resultIPage;
    }

    /**
     * 活动结果地址更新
     *
     * @param req
     * @return
     */
    public boolean updateAwards(ActivityResultReq req) {
        return activityResultService.updateById(new ActivityResult().setActivityResultId(req.getActivityResultId())
                .setUpdatedBy(SecurityUtils.getUserId())
                .setUpdatedTime(new Date())
        );
    }

    public List<ActivityExportVO> activityExport(ActivityDeleteReq req) {
        if (CharSequenceUtil.isBlank(req.getActivityIds())) {
            throw new ServiceException("Export ActivityIds is empty.");
        }
        List<String> brandTasteIdList = Arrays.asList(req.getActivityIds().split(","));
        if (brandTasteIdList.size() > 500) {
            throw new ServiceException("Export ActivityIds size is bigger then 500.");
        }
        LambdaQueryWrapper<Activity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper
                .eq(Activity::getDeletedVersion, 0L)
                .in(Activity::getActivityId, Arrays.asList(req.getActivityIds().split(",")));
        List<Activity> list = activityService.list(lambdaQueryWrapper);
        List<ActivityExportVO> result = ActivityMapping.INSTANCE.coverEntityToExportList(list);
        int i = 1;
        for (ActivityExportVO vo : result) {
            vo.setSeqNo(i);
            i++;
            // 设置活动平台
            tenantFilterProperties.getBrandTenantList().stream().filter(brandTenantVO -> Objects.nonNull(vo.getTenantId()) &&
                    brandTenantVO.getTenantId().equals(vo.getTenantId())).findFirst().ifPresent(brandTenantVO -> {
                vo.setBrandPlatform(brandTenantVO.getBrandNo());
            });
            // 设置开始结束时间组合
            String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, vo.getStartTime());
            String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, vo.getEndTime());
            if (vo.getStartTime().compareTo(new Date()) > 0) {
                vo.setStatus(ActivityStatusEnum.RESERVE_ENABLE.getName());
            } else if ((new Date()).compareTo(vo.getStartTime()) >= 0 && (new Date()).compareTo(vo.getEndTime()) < 0) {
                vo.setStatus(ActivityStatusEnum.IN_PROGRESS.getName());
            } else {
                vo.setStatus(ActivityStatusEnum.OTHER.getName());
            }
            vo.setStartAndEndTime(startTime + " 至 " + endTime);
        }
        return result;
    }

    /**
     * 批量修改活动结果
     *
     * @param list
     * @return
     */
    public boolean awardsListUpdate(List<ActivityResultReq> list) {
        if (CollUtil.isEmpty(list)) {
            return false;
        }
        List<Long> activityResultIds = list.stream().map(ActivityResultReq::getActivityResultId).collect(Collectors.toList());
        if (CollUtil.isEmpty(activityResultIds)) {
            return false;
        }
        // 查询获奖结果数据，并生成发货数据
        List<ActivityResult> activityResults = activityResultService.list(new LambdaQueryWrapper<ActivityResult>()
                .eq(ActivityResult::getDeletedVersion, 0L)
                .in(ActivityResult::getActivityResultId, activityResultIds)
        );
        if (CollUtil.isEmpty(activityResults)) {
            throw new ServiceException("The activity result data is empty");
        }
        // 已发货列表
        List<ActivityResult> grantedList = activityResults.stream().filter(item -> item.getPrizeGrantStatus() != null && item.getPrizeGrantStatus().equals(1)).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(grantedList)) {
            throw new ServiceException("There is shipped data and cannot be updated");
        }
        // 获奖用户列表
        List<Long> userIds = activityResults.stream().map(ActivityResult::getCreatedBy).collect(Collectors.toList());
        // 查询默认地址
        List<AddressUserListDTO> userDefaultAddressList = userAddressService.getDefaultAddressList(userIds);
        if (CollUtil.isEmpty(userDefaultAddressList)) {
            throw new ServiceException("The user's default address is empty");
        }
        Map<Long,Long> activityResultMap = activityResults.stream()
                .collect(Collectors.toMap(ActivityResult::getActivityResultId, ActivityResult::getCreatedBy, (k1, k2) -> k1));
        createDelivery(userDefaultAddressList,activityResultMap);

        List<ActivityResult> activityResultList = Lists.newArrayList();
        list.forEach(activityResultReq ->
                activityResultList.add(new ActivityResult().setActivityResultId(activityResultReq.getActivityResultId())
                        .setPrizeGrantStatus(activityResultReq.getPrizeGrantStatus())
                        .setUpdatedBy(SecurityUtils.getUserId())
                        .setUpdatedTime(new Date()))
        );
        activityResultService.updateBatchById(activityResultList);
        return true;
    }

    private void createDelivery(List<AddressUserListDTO> userDefaultAddressList,Map<Long,Long> activityResultMap) {
        List<ActivityDelivery> deliveries = Lists.newArrayList();
        activityResultMap.forEach((activityResultId,userId)->{
            AddressUserListDTO addressObj = userDefaultAddressList.stream().filter(item -> item.getCreatedBy().equals(userId)).findFirst().orElse(null);
            if (Objects.isNull(addressObj)) {
                throw new ServiceException("The user's default address is empty");
            }
            ActivityDelivery delivery = ActivityDelivery.builder()
                    .objectId(activityResultId)
                    .activityDeliveryId(IdWorker.getId())
                    .createdBy(SecurityUtils.getUserId())
                    .createdTime(new Date())
                    .addressObj(addressObj)
                    .deliverySource(ActivityConstant.DELIVERY_SOURCE_ACTIVITY)
                    .build();
            deliveries.add(delivery);
        });
        activityDeliveryService.saveBatch(deliveries);
    }

    public Boolean activityHidden(Long activityId, Integer hide) {
        // 显示
        activityService.update(new LambdaUpdateWrapper<Activity>()
                .eq(Activity::getActivityId, activityId)
                .set(Activity::getHasHide, hide)
                .set(Activity::getUpdatedBy, SecurityUtils.getUserId())
                .set(Activity::getUpdatedTime, new Date())
        );
        // TODO:  缓存数据处理  （统一处理）
        return true;
    }
    // 定义常量代表未登录状态和无效的参与次数
    private static final long USER_NOT_LOGGED_IN = 0L;
    private static final int INVALID_ATTEND_NUMBER = 0;

    /**
     * 活动可参与次数
     *
     * @param req
     * @return
     */
    public Integer attendNum(LotteryReq req) {
        if (req == null || req.getActivityId() == USER_NOT_LOGGED_IN) {
            logError("用户未登录");
            return INVALID_ATTEND_NUMBER;
        }
        Activity activityBaseInfo = getActivityBaseInfo(req.getActivityId());
        if (Objects.isNull(activityBaseInfo) || activityBaseInfo.getAttendNumber() <= INVALID_ATTEND_NUMBER) {
            logError("活动未配置可参与次数");
            return INVALID_ATTEND_NUMBER;
        }
        if (!activityConfig.getSpecialActivityId().equals(req.getActivityId())
                && userCacheService.verifyPoints(SecurityUtils.getUserId(), activityBaseInfo.getPoints())) {
            return INVALID_ATTEND_NUMBER;
        }
        Integer curAttendNum = getCurrentAttendNum(req, req.getActivityId());
        if (Objects.isNull(curAttendNum)) {
            // 如果无法获取已参与次数，则默认用户尚未参与
            curAttendNum = INVALID_ATTEND_NUMBER;
        }
        int remainingAttendNum = activityBaseInfo.getAttendNumber() - curAttendNum;
        return Math.max(remainingAttendNum, INVALID_ATTEND_NUMBER);
    }

    private Activity getActivityBaseInfo(Long activityId) {
        return activityCacheService.getActivityBaseInfo(activityId);
    }

    private Integer getCurrentAttendNum(LotteryReq req,  Long activityId) {
        if (activityConfig.getSpecialActivityId().equals(activityId)) {
            if (CharSequenceUtil.isBlank(req.getParam())) {
                logError("特殊线下活动，但是二维码param参数为空");
                return null;
            }
            return activityCacheService.getActivityAttendNum(activityId, req.getParam());
        } else {
            return activityCacheService.getActivityAttendDailyNum(activityId, SecurityUtils.getUserId());
        }
    }
    private void logError(String message) {
        log.error(message);
    }

    public Boolean lotteryVerify(Long activityId) {
        // 分支判定： 1. 抽奖次数是否已用完 2. 抽奖次数是否已用完 3. 奖品库存是否已用完
        Activity activityBaseInfo = activityCacheService.getActivityBaseInfo(activityId);
        // 活动有效性校验
        if ((Objects.isNull(activityBaseInfo.getEndTime()) ||
                Objects.isNull(activityBaseInfo.getStartTime()) ||
                activityBaseInfo.getEndTime().before(new Date()) ||
                activityBaseInfo.getStartTime().after(new Date()))
        ) {
            log.error("活动未开始或已过期");
            throw new ServiceException(propertiesTools.getProperties(ACTIVITY_TIME_ERROR));
        }
        // TODO: 积分是否充足判定
        if (userCacheService.verifyPoints(SecurityUtils.getUserId(), activityBaseInfo.getPoints())) {
            log.error("个人积分余额不存在 | 余额小于当前积分");
            return false;
        }
        return true;
    }
}
